##########################################
#                                        #  
#Global variables:                       # 
#                                        # 
##########################################
$VMState=       @{"Running"=2 ; "Stopped"=3 ; "Paused"=32768 ; "Suspended"=32769 ; "Starting"=32770 ; "Snapshotting"=32771 ; "Saving"=32773  ; "Stopping"=32774 }
$ReturnCode=    @{"0"="OK" ; "4096"="Job Started" ; "32768"="Failed"; "32769"="Access Denied" ; "32770"="Not Supported"; "32771"="Unknown" ; "32772"="Timeout" ; "32773"="Invalid parameter" ;
                  "32774"="System is in use" ; "32775"="Invalid state for this operation" ; "32776"="Incorrect data type" ; "32777"="System is not available" ; "32778"="Out of memory" }
$BootMedia=     @{"Floppy"=0 ; "CD"=1 ; "IDE"=2 ; "NET"=3 }
$StartupAction =@{"None"=0 ; "RestartOnly"=1 ; "AlwaysStartup"=2}
$ShutDownAction=@{"TurnOff"=0 ; "SaveState"=1 ; "ShutDown"=2}
$Recoveryaction=@{"None"=0 ; "Restart"=1 ; "RevertToSnapShot"=2}


###################################################################
#                                                                 #
# Helper Functions - not related to any specific Hyper-V objects  #
#                                                                 #
###################################################################


Function Choose-List
{Param ($InputObject, $Property, [Switch]$multiple)
 $Global:counter=-1
 $Property=@(@{Label="ID"; Expression={ ($global:Counter++) }}) + $Property
 if ($inputObject -is [Array]) {
     $InputObject | format-table -autosize -property $Property | out-host
     if ($multiple) { $InputObject[ [int[]](Read-Host "Which one(s) ?").Split(",")] }
     else           { $InputObject[        (Read-Host "Which one ?")              ] }}
 else {$inputObject}
}


Function Out-Tree
{Param ($items, $startAt, $path=("Path"), $parent=("Parent"), $label=("Label"), $indent=0)
 $children = $items | where-object {$_.$parent -eq $startAt.$path.ToString()} 
 if ($children -ne $null) {("| " * $indent) + "+$($startAt.$label)" 
                            $children | ForEach-Object {Out-Tree $items $_ $path $parent $label ($indent+1)} }
 else                     {("| " * ($indent-1)) + "|--$($startAt.$label)" }
}


Function Choose-Tree
{Param ($items, $startAt, $path=("Path"), $parent=("Parent"), 
        $label=("Label"), $indent=0, [Switch]$multiple)
 if ($Indent -eq 0)  {$Global:treeCounter = -1 ;  $Global:treeList=@() ; $Leader="" }
 $Global:treeCounter++
 $Global:treeList=$global:treeList + @($startAt)
 $children = $items | where-object {$_.$parent -eq $startat.$path.ToString()} 
 if   ($children -ne $null) 
      {  $leader = "| " * ($indent) 
         "{0,-4} {1}+{2} "-f  $Global:treeCounter, $leader , $startAt.$label | Out-Host
        $children | sort-object $label | 
           ForEach-Object {Choose-Tree -Items $items -StartAt $_ -Path $path `
                           -parent $parent -label $label -indent ($indent+1)}
      }
 else {  $leader = "| " * ($indent-1) 
        "{0,-4} {1}|--{2} "-f  $global:Treecounter, $leader , $startAt.$Label  | out-Host }
 if ($Indent -eq 0) {if ($multiple) { $Global:treeList[ [int[]](Read-Host "Which one(s) ?").Split(",")] }
                     else           {($Global:treeList[ (Read-Host "Which one ?")]) }  
                    } 
}


Filter Convert-DiskIDtoDrives
{Param ($diskIndex)
 if ($diskIndex -eq $null) {$diskIndex = $_}
 Get-WmiObject -Query "Select * From Win32_logicaldisktoPartition Where __PATH Like '%disk #$diskIndex%' " | % {$_.dependent.split("=")[1].replace('"','')}  
}
# Example: Convert-DiskIDtoDrives 2 
#          Returns the Drive letters of the partions on Disk 2 (in the form D:, E: - with the colon, and no backslash)


Filter Test-WMIJob 
{  param  ($JobID, [Switch]$Wait, $Description="Job")   
   if ($jobID -eq $null) {$jobID = $_}  
   $Job = [WMI]$JobID
   if ($job -ne $null) {
	while (($job.jobstate -eq 4) -and $wait) { 
            Write-Progress -activity ("$Description $($Job.Caption)") -Status "% complete" -PercentComplete $Job.PercentComplete 
            Start-Sleep -seconds 1 
            $Job.PSBase.Get() }  
	$Description +": " + @{2="New"; 3="Starting"; 4="Running"; 5="Suspended"; 6="Shutting Down"; 7="Completed";8="Terminated";9="Killed";10="Exception";11="Service"}[[int]$Job.JobState]
}}


#########################################################
#                                                       #
# Functions for Managing Virtual Hard disk (VHD) files  #
#                                                       #
#########################################################
# TODO: Functions for the following Methods (See http://msdn2.microsoft.com/en-us/library/cc136845(VS.85).aspx ) 
# MergeVirtualHardDisk ,  ExpandVirtualHardDisk, ConvertVirtualHardDisk
# ReconnectParentVirtualHardDisk,  CreateVirtualFloppyDisk ,  ValidateVirtualHardDisk


Function Get-VhdDefaultPath
{ param ($server=".") 
 (Get-WmiObject -computerName $server -NameSpace "root\virtualization" -Class "MsVM_VirtualSystemManagementServiceSettingData").DefaultVirtualHardDiskPath
}

         
Function New-VHD
{Param ([String]$vhdPath=$(Throw("You must specify a Path for the disk")) , [int64]$size, $parentDisk, $server="." ,[Switch]$Fixed )
   # we're going to call the Create xxx Virtual Hard disk method of the Image Management service
   # According to MSDN it's parameters are as follows 
   #   uint32 CreateDynamicVirtualHardDisk(
   #     [in]   string Path,
   #     [in]   uint64 MaxInternalSize (for fixed and dynamic) or String ParentPath for differencing
   #     [out]  CIM_ConcreteJob Ref Job);  
 $ImgMgtSvc=Get-WmiObject -computerName $server -NameSpace  "root\virtualization" -Class "MsVM_ImageManagementService"
 if ($parentDisk -ne $null) {$arguments = @($vhdPath,$parentDisk,$null)
                             $result= $ImgMgtSvc.PSbase.InvokeMethod("CreateDifferencingVirtualHardDisk",$arguments) }
 else { if ($size -lt 1gb) {Throw("You must Specify a disk Size")}
        $arguments = @($vhdPath,$size,$null)
        if ($fixed)  
           { $result=$ImgMgtSvc.PSbase.InvokeMethod("CreateFixedVirtualHardDisk",$arguments ) }
        else 
           { $result=$ImgMgtSvc.PSbase.InvokeMethod("CreateDynamicVirtualHardDisk",$arguments ) }
      }
 "VHD Creation: " + $ReturnCode[[string]$result] | Out-host 
 $arguments[2] 
}
# Example: New-VHD "$(Get-VhdDefaultPath)\tenby.vhd" 20GB
#          Creates a 20GB Dynamic VHD named "tenby.vhd" in the default VHD folder


Filter Mount-VHD
{Param ($vhdPath , [Switch]$offline)
# Does not make sense to remote mount/unmount a disk
 if ($VHDPath -eq $null) {$VHDPath = $_}
 if ($VHDPath -is [Array]) {if ($offline) {$VHDPath | ForEach-Object {Mount-vhd -VHDPath $_ offline} }
                                     else {$VHDPath | ForEach-Object {Mount-vhd -VHDPath $_  } } }
 if ($vhdpath -is [System.IO.FileInfo]) {$VHDPath = $vhdPath.fullName}
 If (test-path $VHDPath) {
     $ImgMgtSvc = Get-WmiObject -NameSpace  "root\virtualization" -Class "MsVM_ImageManagementService"
     $result=$ImgMgtSvc.mount($vhdPath)
     if ($result.returnValue -eq 4096) 
          {test-wmijob $result.job -wait -Description "Disk mount"
           Write-Progress -activity "Disk Mount" -Status "Checking for mounted disk" 
           Start-sleep 2
           $MountedDiskImage= Get-WmiObject -NameSpace "root\virtualization" -query ("Select * from Msvm_MountedStorageImage where name ='"+  $Vhdpath.replace("\","\\")  +"'")
           $diskIndex=(Get-WmiObject -Query "Select * From win32_diskdrive Where Model='Msft Virtual Disk SCSI Disk Device' and ScsiTargetID=$($MountedDiskImage.TargetId) and ScsiLogicalUnit=$($MountedDiskImage.Lun) and scsiPort=$($MountedDiskImage.PortNumber)").index
           if ($diskIndex -eq $null) {"Disk not found (yet)." | out-Host }
           elseif (-not $offline)    {Write-Progress -activity "Disk Mount" -Status "Bringing Disk on-line" 
                                      @("select disk $diskIndex", "online disk" , "attributes disk clear readonly", "exit")  | Diskpart | Out-Null
                                      Start-Sleep -Seconds 5 
                                      Convert-DiskIDtoDrives -diskIndex $diskIndex | out-host }
          write-host -noNewLine "Disk ID... "  
          $diskIndex
          }
     else {"Mount Failed: code $($ReturnCode[[Strring]$result.returnValue])" }}
 $vhdpath=$null
}
# Example 1: dir "$(Get-VhdDefaultPath)\*.vhd" | Mount-Vhd -offline
#            Mounts all  VHDs in the default folder in an offline State and returns the IDs of the disks
# Example 2: Mount-Vhd  (get-VHDdefaultPath) +"\tenby.vhd"              
#            Mounts the VHD, brings it online and echos the Drive letters (if any), and returns the ID of the disk 


Filter UnMount-VHD
{Param ($vhdPath )
# Does not make sense to remote mount/unmount a disk
 if ($VHDPath -eq $null) {$VHDPath = $_}
 if ($VHDPath -is [Array]) {if ($offline) {$VHDPath | ForEach-Object {Mount-vhd -VHDPath $_ offline} }
                                     else {$VHDPath | ForEach-Object {Mount-vhd -VHDPath $_  } } }
 if ($vhdpath -is [System.IO.FileInfo]) {$VHDPath = $vhdPath.fullName}
 If (test-path $VHDPath) {
     $ImgMgtSvc = Get-WmiObject -NameSpace  "root\virtualization" -Class "MsVM_ImageManagementService"
     $result=$ImgMgtSvc.Unmount($vhdPath)
     If ($result.ReturnValue -eq 0) {"Unmounted $vhdPath"} else {"Failed to Unmount $vhdPath, return code: $($result.ReturnValue)" }}
 $vhdpath=$null
}
# Example 1: UnMount-Vhd  (get-VHDdefaultPath) +"\tenby.vhd"
#            UnMounts the VHD 
# Example 2: dir "$(Get-VhdDefaultPath)\*.vhd"  | UnMount-Vhd  
#            Attempts to unmount all the disks in the folder - will fail gracefully if they are not mounted


Function Compact-VHD
{Param ([String]$vhdPath=$(Throw("You must specify a Path for the VHD")) ,  $Server=".")
 $ImgMgtSvc=Get-WmiObject -computerName $server -NameSpace  "root\virtualization" -Class "MsVM_ImageManagementService"
 $result=$ImgMgtSvc.psbase.InvokeMethod("CompactVirtualHardDisk",@($vhdPath))
 $ReturnCode[[string]$result] | Out-host 

}
# example: Compact-Vhd   (get-VHDdefaultPath) +"\tenby.vhd"
#          Compacts the VHD. you can check status with Get-WmiObject -NameSpace root\virtualization msVM_storagejob | ft jobStatus, description, percentcomplete -auto
#          Be aware percent complete doesn't update smoothly


Filter  Get-VHDInfo 
{param ($vhdPath, $server=".")
   if ($vhdPath -eq $null) {$vhdPath = $_}
   if ($vhdPath -is [Array]) {$vhdPath | foreach-Object { Get-VHDInfo  -VHDPath $_ -Server $Server} }
   if ($VhdPath -is [String]) { 
	 $ImgMgtSvc=Get-WmiObject -computerName $server -NameSpace  "root\virtualization" -Class "MsVM_ImageManagementService"
         $ARGUMENTS =@($VHDPath,$NULL)
         $Result=$ImgMgtSvc.Psbase.InvokeMethod("GetVirtualHardDiskInfo",$arguments)
         ([xml]$ARGUMENTS[1]).SelectNodes("/INSTANCE/PROPERTY")  | foreach -begin { $KVPObj = New-Object -TypeName System.Object } `
                                                                         -process {Add-Member -inputObject $KvpObj -MemberType NoteProperty -Name $_.Name -Value $_.value} `
                                                                             -end {$KvpObj} }
   $vhdPath=$null
}


Function Convert-WimtoVHD
{Param ($vhdPath, $cdPath, $SKUID)
 New-VHD $vhdPath 
 $diskIndex=Mount-VHD $vhdPath 
 @("select disk $diskIndex" , "create partition primary" , "active" , "format fs=ntfs quick" , "assign" , "exit") | diskpart
# work in progress I haven't converted this to powershell yet
 & cmd "/c" "makeVHDBoot.bat" $(convert-DiskIDtoDrives $diskIndex) $CDPath $SKUID
}


####################################################
#                                                  #
# Functions for managing VM information / status   #
#                                                  #
####################################################

Function Get-VM 
{Param ($machineName="%", $Server=".", [Switch]$suspended, [switch]$running, [Switch]$stopped) 
 $machineName=$machineName.replace("*","%")
 $WQL="Select * From MsVM_ComputerSystem Where ElementName Like '$machineName' AND Caption Like 'Virtual%' "
 if ($running -or $stopped -or $suspended) {
    [String]$state = ""
    if ($running)  {$State +="or enabledState=" +  $VMState["running"]  }
    if ($Stopped)  {$State +="or enabledState=" +  $VMState["Stopped"]  }
    if ($suspended){$State +="or enabledState=" +  $VMState["suspended"]}
    $WQL += "AND (" + $state.substring(3) +")" }
 Get-WmiObject -computername $Server -NameSpace "root\virtualization" -Query $WQL
}
#Example 1: Get-VM
#           Returns WMI MsVM_ComputerSystem objects for all Virtual Machines (n.b. Parent Partition is filtered out)
#Example 2: Get-VM "Windows 2008 Ent Full TS"   
#	    Returns a single WMI MsVM_ComputerSystem object for the VM named "Server 2008 ENT Full TS"
#Example 3: Get-VM "%2008%"  -Server James-2088
#       or: Get-VM "*2008*" 
#	    Returns WMI MsVM_ComputerSystem objects for VMs containing 2008 in their name on the server James-2008 (n.b. WQL Wild card is %, function converts * to %) 


Function Convert-VmState
{Param ($ID) 
 ($vmState.GetEnumerator() | where {$_.value -eq $ID}).name 
}


Filter Get-VMKVP
{Param ($vm="%", $Server=".")
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array]) {$VM | foreach-Object {get-VMKVP -VM $_ -Server $Server} }
 if ($VM -is [System.Management.ManagementObject]) { 
     $KVPComponent=(Get-WmiObject -computername $VM.__Server -Namespace root\virtualization -query "select * from Msvm_KvpExchangeComponent where systemName = '$($vm.name)'")
     if ($KVPComponent -is [system.management.managementobject]) {$KVPComponent.GuestIntrinsicExchangeItems | 
                                                                  forEach -begin { $KVPObj = New-Object -TypeName System.Object } `
                                                                           -process {([xml]$_).SelectNodes("/INSTANCE/PROPERTY")  | forEach -process {if ($_.name -eq "Name") {$propName=$_.value}; if  ($_.name -eq "Data") {$Propdata=$_.value} } -end {Add-Member -inputObject $KvpObj -MemberType NoteProperty -Name $PropName -Value $PropData}} -end {$KvpObj} }}
}
# Example 1: (Get-VMKVP  "Windows 2008 Ent Full TS").OSName 
#            Returns "Windows Server (R) 2008 Enterprise" - the OS that server is running
# Example 2: Get-vmkvp % -server james-2008
#            Returns the Key Value pairs sent back by all the VMs on the Server James-2009
# Note. The values sent to the child VM can be found in HKLM:\SOFTWARE\Microsoft\Virtual Machine\guest\Parameters 
#       Those sent by the Child VM are in HKLM:\SOFTWARE\Microsoft\Virtual Machine\auto
#       If the VM isn't running its Key/Value Pair Exchange Service does NOT persist the values. So stopped VMs won't return anything !


Filter Ping-VM
{Param ($vm="%", $Server=".")
 $PingStatusCode=@{0="Success" ; 11001="Buffer Too Small" ; 11002="Destination Net Unreachable" ; 11003="Destination Host Unreachable" ; 11004="Destination Protocol Unreachable"
                   11005="Destination Port Unreachable";11006="No Resources";11007="Bad Option";11008="Hardware Error";11009="Packet Too Big"; 11010="Request Timed Out";
                   11011="Bad Request"; 11012="Bad Route"; 11013="TimeToLive Expired Transit"; 11014="TimeToLive Expired Reassembly"; 11015="Parameter Problem";
                   11016="Source Quench"; 11017="Option Too Big"; 11018="Bad Destination"; 11032="Negotiating IPSEC"; 11050="General Failure" }
 if ($VM -eq $null)    {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array])  {$VM | foreach-Object {Ping-VM $_ -Server $Server} }
 if ($VM -is [System.Management.ManagementObject]) { 
     if ($VM.EnabledState -ne $vmstate["running"]) {
            $vm | Select-object -property @{Name="VMName"; expression={$_.ElementName}}, 
                                          @{Name="FullyQualifiedDomainName"; expression={$null}} , 
                                          @{name="NetworkAddress"; expression={$null}} ,
                                          @{Name="Status"; expression={"VM $(Convert-VmState -ID $_.EnabledState)"}} }            
     else {
            $vmFQDN=(Get-VMKVP $VM).fullyQualifiedDomainName
            if ($vmFQDN -eq $null) {
            $vm | Select-object -property @{Name="VMName"; expression={$vm.ElementName}},
                                          @{Name="FullyQualifiedDomainName"; expression={$null}} , 
                                          @{name="NetworkAddress"; expression={$null}} ,
                                          @{Name="Status"; expression={"Could not discover VM's FQDN"}} }           
            else {
                   Get-WmiObject -query "Select * from  Win32_PingStatus where Address='$VmFQDN' and ResolveAddressNames = True and recordRoute=1" |
                   Select-object -property @{Name="VMName"; expression={$vm.ElementName}},
                                           @{Name="FullyQualifiedDomainName"; expression={$vmFqdn}} , 
                                           @{name="NetworkAddress"; expression={$_.ProtocolAddressResolved}} , ResponseTime , ResponseTimeToLive , StatusCode , 
                                           @{Name="Status"; expression={if ($_.PrimaryAddressResolutionStatus -eq 0) {$PingStatusCode[[int]$_.statusCode]} else {"Address not resolved"}}}
                 }
           }
    }
}

 
Function List-VMState 
{Param ($machineName="%", $Server=".") 
 Get-VM -MachineName $MachineName -Server $Server |
            Format-Table -autoSize @{Label="VM Name"; Expression={$_.ElementName}} ,
                                   @{Label="VM GUID"; Expression={$_.Name}} ,
                                   @{Label="State"  ; Expression={Convert-VmState -ID $_.EnabledState }},
                                   @{Label="FQDN"   ; Expression={(get-vmkvp -vm $_ -Server $server).FullyQualifiedDomainName}}  
}
#Example 1: List-VMState -server james-2008
#           Outputs formatted status for all VMs on the server named "James 2008"
#Example 2: List-VMState "Windows 2008 Ent Full TS"  
#	    Outputs formatted status for all for the VM named "Server 2008 ENT Full TS" on the local server


Function Choose-VM
{param ($Server=".", [Switch]$multiple)
 if ($multiple) {choose-list -InputObject (Get-VM -Server $Server) -Property @(@{Label="VM Name"; Expression={$_.ElementName}}, @{Label="State"; Expression={Convert-VmState -ID $_.EnabledState}}) -multiple }
           else {choose-list -InputObject (Get-VM -Server $Server) -Property @(@{Label="VM Name"; Expression={$_.ElementName}}, @{Label="State"; Expression={Convert-VmState -ID $_.EnabledState}})  }
}
# Example 1: Choose-vm -multiple
#            Lets the user select one or more VMs from a list of those on the local machine
# Example 2: Choose-vm -Server James-2008
#            Lets the user select a single VM from a list of those on the server named "James 2008"


Filter Set-VMState 
{Param ($VM , $state, $Server=".")
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Set-VMState -VM $_ -State $state -Server $Server} }
 if ($VM -is [System.Management.ManagementObject]) {$result = $VM.RequestStateChange($State)
                                                    "Changing state of {0}: {1}." -f $vm.elementName , $ReturnCode[[string]$result.ReturnValue] | Out-host 
						    $Result.Job } 
 $VM=$null
} 
#Example 1. get-vm Core-% |  Set-VMState -state $vmstate.running
#           Starts all VMs whose names start with core-
#Example 2. set-vmstate "core","Tenby" $vmstate.running -Server "James-2008"
#           Starts the VMs named Core and Tenby on the server named James-2008 (note quotes round the parameter are optional)
 

Filter Start-VM
{Param ($VM , [Switch]$wait) 
 if ($VM -eq $null) { $JobID=(Set-VMState -VM $_  -State $vmState.running) }
 else               { $jobID=(Set-VMState -VM $VM -State $vmState.running) }
 if ($wait) {test-wmijob $jobID -wait} 
 else       {$jobid}
}
#Example 1: Start-VM (choose-VM -server James-2008 -multiple) 
#           prompts the user to select one or more of the VMs on the server James-2008,  and starts them 
#Example 2: get-vm | where-object {$_.EnabledState -eq $vmState.Suspended} | start-vm
#           Gets VMs in the suspended (saved) state on the local server and Starts them 


Filter Suspend-VM
{Param ($VM  , [Switch]$wait)
 if ($VM -eq $null) {$jobID=(Set-VMState -VM $_  -State $vmState.Suspended)}
 else               {$jobID=(Set-VMState -VM $VM -State $vmState.Suspended)}
 if ($wait) {test-wmijob $jobID -wait}
 else       {$jobid}
}
#example get-vm -running | suspend-vm -wait ; shutdown -s -t 0
#Gets running vms and puts them into a saved state -waiting until they are saved, then shuts down the host 
# See Start-VM for similar examples.


Filter Stop-VM
{Param ($VM  , [Switch]$wait)
 if ($VM -eq $null) { $jobID=(Set-VMState -VM $_  -State $vmState.Stopped)}
 else               { $jobID=(Set-VMState -VM $VM -State $vmState.Stopped)}
 if ($wait) {test-wmijob $jobID -wait}
 else       {$jobid}
}
# See Start-VM for examples.


Filter Shutdown-VM
{Param ($VM , $Reason="Scripted", $Server=".")
 if ($VM -eq $null)    {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array])  {$VM | ForEach-Object {Shutdown-VM -VM $_ -Reason $Reason -Server $server} }
 if ($VM -is [System.Management.ManagementObject]) {
     $ShutDownComponent=get-wmiobject -computername $vm.__server -namespace "root\virtualization" -query  "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='$($vm.name)' "
     If ($ShutDownComponent -ne $null) {$result=$ShutDownComponent.InitiateShutdown($true,$reason) 
                                        If ($result.returnValue -eq 0) {"Shutdown of '$($vm.elementName) ' started."} else {"Attempt to shutdown '$($vm.elementName)' failed with code $($result.returnValue)."} }
     else  {"Could not get shutdown component for '$($vm.elementName)'."}  }
 $vm=$null
}
#Example 1:  shutdown-vm "Tenby" -server James-2008
#            Invokes an OS shutdown on the VM named Tenby
#Example 2:  get-vm | where-object {$_.EnabledState -eq $vmState.running} | shutdown-vm -Reason "Server Upgrade"
#            Gets VMs in the Running state on the local machine and invokes an OS shutdown on them and logs a reason for shutting them down
#Note this depends on the installation components being installed in the VM


Function New-VMConnectSession
{Param ($VM,$server=(get-content Env:computerName)) 
 if ($VM -eq $null) {$VM=$input}
 if ($VM -is [String]) {& "$Env:ProgramFiles\Hyper-V\VMconnect.exe" $server $VM }
 if ($VM -is [Array]) {$VM | ForEach-Object {New-VMConnectSession -VM $_ } }
 if ($VM -is [System.Management.ManagementObject]) {& "$Env:ProgramFiles\Hyper-V\VMconnect.exe" $Vm.__Server "-G" $VM.Name }
 $vm=$null
}
#Example 1: New-VMConnectSession $tenby
#           Launches a Terminal connection to the VM pointed to by $tenby on the local server.
#Example 2: New-VMConnectSession "tenby" -server James-2008
#           Launches a Terminal connection to the VM Named "tenby" on the server named "James-2008"


#############################################################################################
#                                                                                           #
# Functions for working with VMs and their motherboard settings (inc CPU and Memory)        #
#                                                                                           #
#############################################################################################
# ToDO: Change Boot Order  / Autostart / Autostop Action
#ModifyVirtualSystem(
#  [in]   CIM_ComputerSystem Ref ComputerSystem, // Reference to the virtual computer system #to be modified.
#  [in]   string SystemSettingData, // Embedded instance of the CIM_VirtualSystemSettingData class that describes the modified setting values for the virtual computer system.
#                                   // MSCM_VirtualSystemSettingData
#                                   //  BIOSNumLock            : False
#				    //  BootOrder              : {1, 2, 3, 0}
#				    // MsVM_VirtualSystemGlobalSettingData
#				    //  AutomaticRecoveryAction  
#				    //  AutomaticShutdownAction 
#				    //  AutomaticStartupAction 
#                                   //  SnapshotDataRoot 
#  [out]  CIM_VirtualSystemSettingData Ref ModifiedSettingData, // Reference to the CIM_VirtualSystemSettingData instance that represents the object that was modified.
#  [out]  CIM_ConcreteJob Ref Job);
#If this method is executed synchronously, it returns 0 if it succeeds. 
#If this method is executed asynchronously, it returns 4096 and the Job output parameter can be used to track the progress of the asynchronous operation. 
#Any other return value indicates an error.

Function Get-VMSettingData
{Param ($VM, $Server=".")
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [System.Management.ManagementObject]) {Get-WmiObject -ComputerName $vm.__Server -NameSpace "root\virtualization" -Query "ASSOCIATORS OF {$($VM.__Path)} Where AssocClass = MsVM_SettingsDefineState ResultClass = MsVM_VirtualSystemSettingData"}
}


Function New-VM
{Param([String]$Machinename=$(Throw("You must give a VM Name")) ,  $Server="." )
  # We're going to call Define virtual System This mode of calling doesn't seem to be on MSDN ... 
 $VSMgtSvc=Get-WmiObject -computerName $server -NameSpace "root\virtualization" -Class "MsVM_virtualSystemManagementService"
 $Result=$VSMgtSvc.defineVirtualSystem()
 if ($result.ReturnValue -eq 0) {
     "Created VM" | out-host
     $VSSD=get-wmiobject -computerName $server -namespace root\virtualization -query ("select * from Msvm_VirtualSystemSettingData where systemname= "+  $Result.definedSystem.split("=")[-1] )
 # Now modify the VM to match the parameters 
     $VSSD.ElementName=$machineName
 # Build the arguments array - the path VSSD object in XML form and a bunch of nulls
     $arguments = @($Result.definedSystem , $VSSD.psbase.GetText([System.Management.TextFormat]::WmiDtd20),$null,$null)
 # Invoke the define Virtual System method, passing the arguments.  
     if ($VSMgtSvc.psbase.InvokeMethod("ModifyVirtualSystem", $arguments) -eq 0) {  "Set VM Name" | out-Host
                                                                                     Get-WmiObject -computername $Server -NameSpace "root\virtualization" -Query ("Select * From MsVM_ComputerSystem Where Name="+ $Result.definedSystem.split("=")[-1] )} 
     else {write-error "Couldn't Set VM Name"}}
}
#Example 1: $tenby = new-VM "Tenby"
#	    Creates an New-VM on the local machine using default settings, and setting the name to "Tenby", storing its  MsVM_ComputerSystem object in the variable $tenby
#Example 2: $tenby = new-VM "Tenby" -server "James-2008"
#	    Ditto but on a server named "James-2008"


Function Set-VM
{Param($VM, $MachineName, $bootOrder, $notes, $AutoRecovery, $AutoShutDown, $autoStartup, $Server="." )
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [System.Management.ManagementObject]) {
     $VSMgtSvc=Get-WmiObject -computerName $server -NameSpace "root\virtualization" -Class "MsVM_virtualSystemManagementService"
 
     if (($machineName -ne $null) -or ($notes -ne $null) -or ($BootOrder -ne $null)) { 
         $VSSD=(get-wmiobject -computerName $server -namespace root\virtualization -query "ASSOCIATORS of {$($VM.__Path)} Where AssocClass=MsVM_SettingsDefineState ResultClass = MsVM_VirtualSystemSettingData")
         if ($machineName -is [String])  {$VSSD.ElementName=$machineName}
         if ($notes       -ne [String])  {$VSSD.notes=$Notes}
         if ($BootOrder   -is [array])   {if (($bootOrder | group | where {$_.count -gt 1}) -ne $null) {"An item appeared twice in the boot order"}
                                  else {$VSSD.BootOrder=$BootOrder} }
         # Build the arguments array - the path VSSD object in XML form and a bunch of nulls
         $arguments = @($VM , $VSSD.psbase.GetText([System.Management.TextFormat]::WmiDtd20),$null,$null)
         # Invoke the define Virtual System method, passing the arguments.  
         if   ($VSMgtSvc.psbase.InvokeMethod("ModifyVirtualSystem", $arguments) -eq 0) { "Modified VM Settings object" } 
         else                                                                          {write-error "Could not Modify VM Settings Object"}}
     If (($AutoRecovery -ne $null) -or ($AutoShutDown -ne $null) -or ($AutoStartup -ne $null)) {
        $VSGSD=(get-wmiobject  -namespace root\virtualization -query "select * from Msvm_VirtualSystemglobalSettingData where instanceId = 'Microsoft:$($vm.name)\\global'")
        if ($AutoRecovery -is [int]) {$VSGSD.AutomaticRecoveryAction         = $AutoRecovery }
        if ($AutoShutDown -is [int]) {$VSGSD.AutomaticShutdownAction         = $AutoShutdown }
        if ($AutoStartup  -is [int]) {$VSGSD.AutomaticStartupAction = $autoStartup  }
         $arguments = @($VM , $VSGSD.psbase.GetText([System.Management.TextFormat]::WmiDtd20),$null,$null)
         # Invoke the define Virtual System method, passing the arguments.  
         if   ($VSMgtSvc.psbase.InvokeMethod("ModifyVirtualSystem", $arguments) -eq 0) { "Modified VM Global Settings object" } 
         else                                                                          {write-error "Could not Modify VM Global Settings Object"}}}
}
# example 1:  Set-vm $vm -bootorder $bootmedia["CD"],$bootmedia["IDE"],$bootmedia["net"],$bootmedia["Floppy"]
#             Sets the boot order for the machine whose config is $vm to CD, IDE, Network, Floppy. 
# example 2:  set-vm -vm core -bootOrder @(3,2,0,1) 
#             Sets the boot order for the VM named Core to Network, IDE, Floppy, CD
# Example 3:  set-vm -vm core -autoStart $StartupAction["AlwaysStartup"]
#             Sets the VM named Core to always Startup 
# Example 4:  Set-vm $vm -bootorder $bootmedia["CD"],$bootmedia["IDE"],$bootmedia["net"],$bootmedia["Floppy"] -autoStart 0 -AutoShutdown 2
#             Sets the boot order for the machine whose config is $vm AND sets it to never autostart and to close down the OS on shutdown 
# UNTESTED Example: Set-vm "CORE-%" -bootorder $bootmedia["CD"],$bootmedia["IDE"],$bootmedia["net"],$bootmedia["Floppy"] -autoStart $StartupAction["AlwaysStartup"]
#             Should take all the machines whose names begin CORE- and set their boot order and start-up action




Filter Remove-VM
{Param ($VM ,  $server=".")
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Set-VMMemory -VM $_ -Server $Server} }
 if ($VM -is [System.Management.ManagementObject]) {
     $VSMgtSvc=Get-WmiObject -computerName $VM.__server -NameSpace "root\virtualization" -Class "MsVM_virtualSystemManagementService"
     $arguments = @($vm.__path,$null)
     $vmName=$vm.elementname
     $result = $VSMgtSvc.psbase.InvokeMethod("DestroyVirtualSystem", $arguments)
     if ($result -eq 0) {"Deleted VM '$vmName'." | out- host} 
     else  {if ($result -eq 4096) {"Job Started to delete VM '$vmName'." | out-host
				   $arguments[1]}
            else                  {Write-error "Failed to delete VM '$vmname', return code: $result." } }
     $vm=$null}
}
#Example 1: Remove-VM "Tenby" -server James 2008
#	    Removes the VM named Tenby from the server named "James-2008"
#Example 2: Get-vm | remove-vm 
#	    Removes all the VMs on the local machine. No prompts. No warnings and -WhatIf is ignored. 


Filter Set-VMMemory
{Param ($VM , $memory, $server=".")
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Set-VMMemory -VM $_ -Memory $memory -Server $Server} }
   # We're going to call Modify Virtual System Resources, and we need set up the parameter array - following info is copied from MSDN
   #   uint32 ModifyVirtualSystemResources(
   #     [in]   CIM_ComputerSystem Ref ComputerSystem,  // Reference to the virtual computer system whose resources are to be modified.
   #     [in]   string ResourceSettingData[],           // Array of embedded instances of the CIM_ResourceAllocationSettingData class 
   #                                                    //  that describe the resources to be modified 
   #     [out]  CIM_ConcreteJob Ref Job,                //  Optional reference that is returned if the operation is executed asynchronously. );
 if ($VM -is [System.Management.ManagementObject]) {
     # Step 1 Get a MsVM_MemorySettingData object - a subclass of Resource-Allocation-Setting-Data
     $memSettingData=Get-WmiObject -computerName $vm.__server -NameSpace  "root\virtualization" -Query "select * from Msvm_MemorySettingData where instanceId Like 'Microsoft:$($vm.name)%' "
     $memsettingData.Limit           =$Memory / 1MB
     $memsettingData.Reservation     =$Memory / 1MB
     $memsettingData.VirtualQuantity =$Memory / 1MB
 
     # Step 2 build the arguments array - the WMI path for the server, the Memory Setting Data object in XML form and a null for the job ID
     $arguments=@($VM.__Path, @($memsettingData.psbase.GetText([System.Management.TextFormat]::WmiDtd20)) , $null)
     $VSMgtSvc = (Get-WmiObject -computerName $vm.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService") 
     #          invoke its define Modify Virtual System Resources method, passing the arguments.
     # If this method is executed synchronously, it returns 0 if it succeeds. 
     # If this method is executed asynchronously, it returns 4096 and the Job output parameter can be used to track progress  
     # Any other return value indicates an error
     # So return "Success" or "Failure" 
     $result=$VSMgtSvc.psbase.invokeMethod("ModifyVirtualSystemResources",$arguments)  
     if ($result -eq 0) {"Set memory for '$($vm.elementName)' to $memory."} else {"Failed to set memory for '$($vm.elementName)', result code: $result."} }
 $vm=$null
}
#Example 1: set-VMmemory "Tenby" 1.5gb -server James-2008
#           sets the VM named "Tenby" on server James-2008 to have 1.5GB of RAM
#Example 2: Get-vm Core-% | set-VMmemory -memory 1073741824
#           Gives 1GB of RAM to all the VMs on the local server whose names begin CORE-

Filter Get-VMMemory
{Param ($VM , $server=".")
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Get-VMMemory -VM $_ -Server $Server} }
 if ($VM -is [System.Management.ManagementObject]) {
             Get-WmiObject -computerName $vm.__server -NameSpace  "root\virtualization" -Query "select * from Msvm_MemorySettingData where instanceId Like 'Microsoft:$($vm.name)%' " |select-object -property @{Name="VMName"; Expression={$VM.ElementName}} , @{Name="VMGUID"; Expression={$VM.Name}}, VirtualQuantity, Limit, Reservation }
 $vm=$null
}


Filter Set-VMCPUCount
{Param ($VM , $CPUCount, $server=".")
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -server $server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Set-VMCPUCount -VM $_ -CPUCount $CPUCount} }
 if ($VM -is [System.Management.ManagementObject]) {
     # See comments in Set-VMMemory for how this works. This time we're going to get a MsVM_ProcessorSettingData object - a subclass of Resource Allocation Setting Data
     $procsSettingData=Get-WmiObject -computerName $vm.__server -NameSpace  "root\virtualization" -query "select * from MsVM_ProcessorSettingData where instanceID like 'Microsoft:$($vm.name)%' "
     $procsSettingData.VirtualQuantity=$CPUCount
     $arguments=@($VM.__Path, @($procsSettingData.psbase.GetText([System.Management.TextFormat]::WmiDtd20)) , $null)
     $VSMgtSvc = (Get-WmiObject -computerName $vm.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService") 
     $result=$VSMgtSvc.psbase.invokeMethod("ModifyVirtualSystemResources", $arguments)   
     if ($result -eq 0) {"Set CPU Count for '$($vm.elementName)' to $CPUCount."} else {"Failed to set CPUCount for '$($vm.elementName)', result code: $result."}}
 $vm=$null
}
#Example 1: Set-VMCPUCount "tenby" 2 -Server "James-2008"
#           Assigns 2 CPUs to the VM named Tenby on Server James-2008 
#Example 2: Get-vm Core-% | Set-VMCPUCount - CPUCount 2
#           Gives 2 CPUs to all VMs on the local machine whose names begin CORE-


Filter Get-VMCPUCount
{Param ($VM , $server=".")
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Get-VMCpuCount -VM $_ -Server $Server} }
 if ($VM -is [System.Management.ManagementObject]) {
     Get-WmiObject -computerName $vm.__server -NameSpace  "root\virtualization" -query "select * from MsVM_ProcessorSettingData where instanceID like 'Microsoft:$($vm.name)%' " | select-object -property @{Name="VMName"; Expression={$VM.ElementName}} , @{Name="VMGUID"; Expression={$VM.Name}}, VirtualQuantity, Limit, Reservation, Weight,  ProcessorsPerSocket, SocketCount  }
 $vm=$null
}


Function New-VMRasd
{Param ($ResType, $ResSubType , $server=".")
 #Get a Resource Allocation Setting Data object  
 $allocCapsPath= ((Get-WmiObject -ComputerName $server -NameSpace "root\virtualization" -Query "Select * From MsVM_AllocationCapabilities Where ResourceType = $ResType AND ResourceSubType = '$ResSubType'").__Path).replace('\','\\')
 New-Object System.Management.Managementobject((Get-WmiObject -ComputerName $server -NameSpace "root\virtualization" -Query "Select * From MsVM_SettingsDefineCapabilities Where  valuerange=0 and Groupcomponent = '$AllocCapsPath'").partcomponent)
}


#######################################################################
#                                                                     #
# Functions for working disk objects , SCSI Controller, Driver, Disk  #
#                                                                     #
#######################################################################


Filter Get-VMDiskController
{Param ($VM , $ControllerID, $server=".", [Switch]$SCSI, [Switch]$IDE )
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -server $Server) }
 if ($VM -is [Array]) {if ($SCSI) {$VM | ForEach-Object {Get-VMDiskController -VM $_ -ControllerID  $ControllerID -SCSI} }
                       if ($IDE)  {$VM | ForEach-Object {Get-VMDiskController -VM $_ -ControllerID  $ControllerID -IDE } } 
                       if ((-not $scsi) -and (-not $IDE) -and ($contollerID -eq $null)) {$VM | ForEach-Object {Get-VMDiskController -VM $_ } } }
 if ($VM -is [System.Management.ManagementObject]) {
     if ((-not $scsi) -and (-not $IDE) -and ($contollerID -eq $null)) {
         if ($vm.__class -eq "Msvm_ComputerSystem") {$vm=(get-vmsettingData $vm)} 
         Get-WmiObject -Query "ASSOCIATORS OF {$vm} where resultclass= Msvm_ResourceAllocationSettingData " -computerName $vm.__server -NameSpace "root\virtualization"  | 
                where {($_.resourcesubtype -eq 'Microsoft Emulated IDE Controller')  -or ($_.resourceSubtype -eq 'Microsoft Synthetic SCSI Controller')}  }
     Else {
         if ($scsi) { $controllers=Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData
                                            Where instanceId Like 'Microsoft:$($vm.name)%'and resourceSubtype = 'Microsoft Synthetic SCSI Controller' " `
                                           -NameSpace "root\virtualization" -computerName $vm.__server
		      if ($controllerID -eq $null) {$controllers}
                      else  {$controllers | select -first ($controllerID + 1)  | select -last 1  }    }
         if ($IDE)  { Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData 
                                            Where instanceId Like 'Microsoft:$($vm.name)%\\$ControllerID%'
                                            and resourceSubtype = 'Microsoft Emulated IDE Controller' " -NameSpace "root\virtualization" -computerName $vm.__server } } }
 $vm=$null
}
#Example 1: Get-VM -server James-2008| Get-VMDiskController -IDE -SCSI
#           Returns all the DiskControllers for all the VMs on Server James-2008      
#Example 2: Get-VMDiskController $Tenby -SCSI -ContollerID 0
#           Returns SCSI controller 0 in the VM pointed to by $Tenby


Filter Get-VMDrive
{Param ($Controller, $LUN )
 if ($Controller -eq $null) {$Controller=$_}
 if ($Controller -is [Array]) {$Controller | ForEach-Object {Get-VMDrive -Controller  $Controller -LUN $Lun} }
 if ($Controller -is [System.Management.ManagementObject]) {
    $CtrlPath=$Controller.__Path.replace("\","\\")
    Get-WmiObject -computerName $controller.__server -Query "Select * From MsVM_ResourceAllocationSettingData Where PARENT='$ctrlPath' and Address Like '$Lun%' " -NameSpace "root\virtualization" }
 $Controller = $null
}
#Example 1: Get-VMDiskController "Tenby" -server "James-2008" -IDE -ContollerID 0 | get-VMDrive
#           Gets the drives attached to IDE controller 0 in the VM named Tenby on Server James-2008
#Example 2: get-VMDrive $controller 0
#           Gets the first drive attached tothe controller pointed to by $controller. 


Filter Get-VMDisk
{Param ($Drive)
 if ($Drive -eq $null) {$Drive=$_}
 if ($Drive -is [Array]) {$Controller | ForEach-Object {Get-VMDrive -Controller  $Controller -LUN $Lun} }
 if ($Drive -is [System.Management.ManagementObject]) {
    $DrivePath=$Drive.__Path.replace("\","\\")
    Get-WmiObject -computerName $drive.__server -Query "Select * From MsVM_ResourceAllocationSettingData Where PARENT='$DrivePath' " -NameSpace "root\virtualization" }
 $Drive = $null
}
#Example 1: Get-VMDiskController "Tenby" -server "James-2008" -IDE -ContollerID 0 | get-VMDrive | Get-VmDisk
#           Gets the disks in the drives attached to IDE controller 0 in the VM named Tenby on Server James-2008
#Example 2: get-VMDisk $drive
#           Gets the disk in the drive pointed to by $drive


Function List-VMDisk
{Param ($vm=(Get-vm), [switch]$snapshot)
 if ($vm -is [String]) {$vm = get-vm -machineName $vm}
 if ($vm -isnot [array]) {$vm=@($vm)}
 if ($snapshot) {$VM= ($VM + (get-vmsnapshot $vm)  | sort elementname) }
 foreach ($v in $vm) {
         if ($v -is [String]) {$v = get-vm -machineName $v}
         foreach ($dc in (get-vmdiskcontroller -vm $v)) {
                 foreach ($drive in (get-vmdrive -controller $dc)) {
                         get-vmdisk -drive $drive | select-object -property `
                                                       @{name="VMName"; expression={$v.elementName}}, 
                                                       @{name="VMGUID"; expression={$v.Name}},
                                                       @{name="ControllerName"; expression={$dc.elementName}},
                                                       @{name="ControllerInstanceID"; expression={$dc.InstanceId}},
                                                       @{name="ControllerID"; expression={$dc.instanceID.split("\")[-1]}}, 
                                                       @{name="DriveName"; expression={$drive.caption}} ,
                                                       @{name="DriveInstanceID"; expression={$drive.instanceID}}, 
                                                       @{name="DriveLUN"; expression={$drive.address}},
                                                       @{name="DiskPath"; expression={$_.Connection}}, 
                                                       @{name="DiskName"; expression={$_.ElementName}}, 
                                                       @{name="DiskInstanceID"; expression={$_.InstanceID}} }}}
}
#Example 1: List-VMDisk (choose-vm -server "James-2008" -multi) | format-table -autosize -property VMname, DriveName,  @{Label="Conected to"; expression={"{0,5} {1}:{2}" -f $_.Controllername.split(" ")[0], $_.ControllerID, $_.DriveLun }} , DiskPath
#           Displays the disks connected to the chosen VMs, giving the VM Name, Hard drive/DVD Drive , Controller:LUN and VHD/ISO file
#Example 2: (List-VMDisk  (get-vm) | foreach {$_.diskpath})
#           Returns a list of all the disk paths used on all the VMs on the Local server 
#Example 3: List-vmdisk | where {$_.diskpath -match "^IDE"} 
#           Finds which VMs are connected to the Physical CD/DVD drive.  


Filter Add-VMSCSIController
{Param ($VM, $server=".")
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -Server $Server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Add-VMSCSIController -VM $_ -server $server } }
 if ($VM -is [System.Management.ManagementObject]) {
   # We're going to call ADD Virtual System Resources, and we need set up the parameter array - following info is copied from MSDN
   # uint32 AddVirtualSystemResources(
   #  [in]   CIM_ComputerSystem REF TargetSystem,    // Reference to the virtual computer system whose resources are to be modified. 
   #  [in]   string ResourceSettingData[],           // Array of embedded instances of the CIM_ResourceAllocationSettingData class 
   #                                                 // that describe the resources 
   #  [out]  CIM_ResourceAllocationSettingData REF NewResources[],
   #  [out]  CIM_ConcreteJob REF Job);
   # so build the arguments array - the WMI path for the server, the Resource allocation Setting Data object in XML form and a null for the job ID
     $SCSIRASD=NEW-VMRasd -ResType 6 -ResSubtype 'Microsoft Synthetic SCSI Controller' -Server $vm.__Server
     $SCSIRASD.elementName="VMBus SCSI Controller"
     $arguments = @($VM.__Path, @( $SCSIRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
     $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
   #  invoke its ADD Virtual System Resources method, passing the arguments. A return code of 0 indicates success
     $result=$VSMgtSvc.psbase.InvokeMethod("AddVirtualSystemResources", $arguments)   
     if ($result -eq 0) {"Added VMBus SCSI Controller to '$($vm.elementName)'."} else {"Failed to add SCSI Controller to '$($vm.elementName)', result code: $result."} }	
 $vm=$null
}
#Example 1: Add-VMSCSIController $tenby 
#           Adds a VMBus SCSI Controller to VM whose info is in $tenby
#Example 2: Get-vm Core-% -server james-2008 | Add-VMSCSIController 
#           Adds a SCSI Controller to all VMs whose names begin CORE- on the server named "James-2008"


Function Remove-VMSCSIcontroller
{Param( $VM, $controllerID, $server="." )
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -Server $Server) }
 $controller= (Get-VMDiskController -vm $vm -ControllerID $ControllerID -SCSI)
   # We're going to call Remove Virtual System Resources, and we need set up t he parameter array - following info is copied from MSDN
   # uint32 RemoveVirtualSystemResources(
   #  [in]   CIM_ComputerSystem REF TargetSystem,
   #  [in]   CIM_ResourceAllocationSettingData REF ResourceSettingData[],
   #  [out]  CIM_ConcreteJob REF Job);
 # to Avoid leaving orphaned objects behind we're going to 
 #   (1) Get the drives bound to the controller, (2) remove the disks inserted into them, (3) Then Remove the drives
 # and finally (4) Remove  the controller
 # So we have to set up the arguments array and call the Remove method more than once. 
 if ($Controller -is [System.Management.ManagementObject]) {
        $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
	foreach ($drive in (get-vmdrive $controller)) {  
	    $disk = ($drive | get-vmdisk) 
            if ($disk -is [System.Management.ManagementObject]) {
                $arguments = @($VM.__Path, @( $disk.__Path ), $null ) 
                $result=$VSMgtSvc.psbase.invokeMethod("RemoveVirtualSystemResources", $arguments) 
                if ($result -eq 0) {"Removed disk from VM '$($vm.elementname)'." }  else {"Failed to remove disk from VM '$($vm.elementname)', result code: $Result." }}	
            $arguments = @($VM.__Path, @( $drive.__Path ), $null ) 
            $result=$VSMgtSvc.psbase.invokeMethod("RemoveVirtualSystemResources", $arguments) 
            if ($result -eq 0) {"Removed drive from VM '$($vm.elementname)'." }  else {"Failed to remove disk from VM'$($vm.elementname)', result code: $Result." }}	
        $arguments = @($VM.__Path, @( $controller.__Path ), $null ) 
        $result=$VSMgtSvc.psbase.invokeMethod("RemoveVirtualSystemResources", $arguments) 
        if ($result -eq 0) {"Removed Controller from VM '$($vm.elementname)'." }  else {"Failed to remove controller from VM '$($vm.elementname)', result code: $Result." }}	
 $Controller = $null
}
#Example 1: Remove-VMSCSIController $tenby 0
#           Remove the first VMBus SCSI Controller to VM whose info is in $tenby


Filter Add-VMDRIVE
{Param ($VM , $ControllerID=0 , $LUN, $Server="." ,   [switch]$DVD , [switch]$SCSI)
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -Server $server) }
 if ($VM -is [Array]) {if ($DVD) {$VM | ForEach-Object {Add-VMDRIVE -VM $_ -ControllerID $ControllerID -LUN $LUN -server $server  -DVD} }
                           else  {if ($scsi) {$VM | ForEach-Object {Add-VMDRIVE -VM $_ -ControllerID $ControllerID -LUN $LUN -server $server} } 
				        else {$VM | ForEach-Object {Add-VMDRIVE -VM $_ -ControllerID $ControllerID -LUN $LUN -server $server -SCSI } } }}
 # think of this as a container into which we mount a virtual disk - makes more sense with DVDs (DVD drive and DVD disk) than Hard disks
 if ($VM -is [System.Management.ManagementObject]) { 
     #Step 1. Get Resource Allocation Setting Data object for a disk, set it's parent to the Controller and the Address to the "Lun"
     if ($DVD)  {$diskRASD=NEW-VMRasd -ResType 16 -ResSubType 'Microsoft Synthetic DVD Drive' -Server $VM.__Server } 
     else       {$diskRASD=NEW-VMRasd -ResType 22 -ResSubType 'Microsoft Synthetic Disk Drive'  -Server $VM.__Server }
     if ($SCSI) {$diskRASD.parent=(Get-VMDiskController -vm $vm -ControllerID $ControllerID -SCSI).__Path }
     else       {$diskRASD.parent=(Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE).__Path  }
     $diskRASD.address=$Lun
     $arguments = @($VM.__Path, @( $diskRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
     $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
     $result=$VSMgtSvc.psbase.invokeMethod("AddVirtualSystemResources", $arguments)   
     if ($result -eq 0) {"Added drive to '$($vm.elementName)'."} else {"Failed to add drive to '$($vm.elementName)', result code: $result."} }
 $vm=$null
}
#Example 1: Add-VMDRIVE "tenby" 1 1 -server james-2008 
#           Adds a virtal DVD to IDE contoller 1, disk slot 1 on the VM named Tenby on Server James-2008
#Example 2: Add-VMDRIVE $tenby 0 3 -SCSI 
#           Adds a virtal hard disk drive to SCSI controller 0, LUN 3 on the VM whose info is in $tenby
#Example 3: Get-vm Core-% | Add-VMDRIVE -controllerID 0 -lun 1 -DVD
#           Adds a DVD drive to  IDE contoller 0, disk slot 1 on all the VMs on the local server whose name begins with CORE-


Function Add-VMDISK
{Param ($VM , $ControllerID=0 , $LUN=0, $VHDPath, $server=".", [switch]$DVD , [switch]$SCSI)
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -Server $Server) }
 # Similar to Adding the drive, but we request a different resource type, and the parent is the 'Microsoft Synthetic Disk Drive', instead of disk controller
 # Mount an ISO in a DVD drive or A VHD in a Disk drive 
 if ($VM -is [System.Management.ManagementObject]) { 
     if ($DVD)  {$diskRASD=NEW-VMRasd -resType 21 -resSubType 'Microsoft Virtual CD/DVD Disk' -server $vm.__Server } 
     else       {$diskRASD=NEW-VMRasd -resType 21 -resSubType 'Microsoft Virtual Hard Disk'   -server $vm.__Server }
     if ($SCSI) {$diskRASD.parent=(Get-VMDrive -controller (Get-VMDiskController -vm $vm -ControllerID $ControllerID -SCSI) -Lun $Lun ).__Path }
     else       {$diskRASD.parent=(Get-VMDrive -controller (Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE)  -Lun $lun ).__Path }
     $diskRASD.Connection=$VHDPath
     $arguments = @($VM.__Path, @( $diskRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
     $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
     $result=$VSMgtSvc.psbase.invokeMethod("AddVirtualSystemResources", $arguments)   
     if ($result -eq 0) {"Added disk to '$($vm.elementName)'."} else {"Failed to add disk to '$($vm.elementName)', result code: $result."} } 
}
#Example 1: Add-VMDisk $tenby 0 1 "C:\update.iso" -DVD
#           Adds a DVD image C:\update.iso, to disk 1, contoller 0 on the VM whose info is in $tenby
#Example 2: Add-VMDisk $tenby 0 0 ((get-VHDdefaultPath) +"\tenby.vhd") 
#           Adds a virtal hard disk named tenby.vhd in the Default folder , to disk 0, contoller 0 on the VM whose info is in $tenby


Function Add-VMNewHardDisk 
{Param ($VM , $ControllerID=0, $LUN=0, $VHDPath, $size, $Server=".", [switch]$fixed, [switch]$SCSI)
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -Server $Server) }
 if ($VM -is [System.Management.ManagementObject]) { 
       if ($parentDisk -ne $null){New-VHD -vhdPath $vhdPath -parentDisk $parentDisk -server $vm.__server }
       else       { if ($fixed)  {New-VHD -vhdPath $vhdPath -size $size -fixed -server $vm.__server }
                           else  {New-VHD -vhdPath $vhdPath -size $size -server $vm.__server }  }
 if ($SCSI) { Add-VMDRIVE -VM $VM -ControllerID  $ControllerID -LUN $LUN -scsi
              Add-VMDISK  -VM $VM -ControllerID  $ControllerID -LUN $LUN -VHDPath $VHDPath -scsi}
 else       { Add-VMDRIVE -VM $VM -ControllerID  $ControllerID -LUN $LUN
              Add-VMDISK  -VM $VM -ControllerID  $ControllerID -LUN $LUN -VHDPath $VHDPath}}
}
#example:  Add-VMNewHardDisk -vm $vm -controllerID 0 -lun 3 -vhdpath "$(get-VHDdefaultPath)\foo31.vhd" -size 20gb -scsi 
#          Adds a 20GB dynamic disk, named foo31.vhd in the default folder,  to the VM defined in $VM, on SCSI controller 0, Lun 3. 


Function Set-VMDisk
{Param( $VM, $controllerID, $LUN, $path, $server="." , [switch]$scsi)
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -server $Server) }
 if ($VM -is [System.Management.ManagementObject]) { 
     if ($SCSI) {  $RASD=(Get-VMDrive -controller (Get-VMDiskController -vm $vm -ControllerID $ControllerID -SCSI) -Lun $Lun ) | get-VMDisk }
     else       {  $RASD=(Get-VMDrive -controller (Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE)  -Lun $lun ) | get-VMDisk }
     if ($RASD -is [System.Management.ManagementObject]) {
         $RASD.connection=$path
         $arguments = @($VM.__Path, @( $RASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null )                                                                                                                                                                                             
         $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
         $result=$VSMgtSvc.psbase.invokeMethod("ModifyVirtualSystemResources", $arguments) 
         if ($result -eq 0) {"Changed disk on VM '$($vm.elementname)'." }	else {"Failed to change disk on VM '$($vm.elementname)', result code: $Result." }}	
     else {"Could not find the specified disk on VM '$($vm.elementname)'"} }
}
#Example 1: Set-VMDisk Tenby 0 1 (Get-WmiObject -Query "Select * From win32_cdromdrive Where ID='D:' " ).deviceID
#           Sets the DVD on controller 0, device 1 for the VM named "Tenby" on the local Server to point to physical drive D: on the host. 
#Example 2: Set-VMDisk $Core 0 0 "\\?\Volume{d1f72a03-d43a-11dc-8bf1-806e6f6e6963}\Virtual Hard Disks\Core.vhd"
#           Sets the Disk on controller 0, device 0 of the VM pointed to by $core to Core.VHD using GUID (not drive letter) notation


Function Remove-VMdrive
{Param( $VM, $controllerID, $LUN, $server="." , [switch]$scsi, [switch]$Diskonly )
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -server $Server) }
 if ($SCSI) {$drive=(Get-VMDrive -controller (Get-VMDiskController -vm $vm -ControllerID $ControllerID -SCSI) -Lun $Lun )}
 else       {$drive=(Get-VMDrive -controller (Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE)  -Lun $lun )}
 if ($drive -is [System.Management.ManagementObject]) {
     $disk=$drive | get-vmdisk
     $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
     if ($disk -is [System.Management.ManagementObject]) {
         $arguments = @($VM.__Path, @( $disk.__Path ), $null ) 
         $result=$VSMgtSvc.psbase.invokeMethod("RemoveVirtualSystemResources", $arguments) 
         if ($result -eq 0) {"Removed disk from VM '$($vm.elementname)'." }  else {"Failed to remove disk from VM '$($vm.elementname)', result code: $Result." }}	
     if (-not $diskOnly) {  
        $arguments = @($VM.__Path, @( $drive.__Path ), $null ) 
        $result=$VSMgtSvc.psbase.invokeMethod("RemoveVirtualSystemResources", $arguments) 
        if ($result -eq 0) {"Removed drive from VM '$($vm.elementname)'." }  else {"Failed to remove disk from VM'$($vm.elementname)', result code: $Result." }}}	
}
#Example 1: Remove-VMdrive "Tenby" 0 1 -SCSI -DiskOnly -Server "James-2008" 
#           Remove the Disk from the drive at device 1 of SCSI controller 0 of the VM named "Tenby" on the Server "James-2008" 
#Example 2: Remove-VMdrive $Core 1 1 -IDE 
#           Remove the Disk and drive at device 1 of IDE controller 1 in the VM pointed to by $core


Function Add-VMFloppyDisk
{Param ($VM , $VFDPath , $server="."  )
 if ($VM -is [String]) {$VM=(Get-VM -machineName $VM -server $Server) }
 if ($VM -is [System.Management.ManagementObject]) { 
     $diskRASD=NEW-VMRasd -resType 21 -resSubType 'Microsoft Virtual Floppy Disk' -server $vm.__Server 
     $diskRASD.parent=(Get-WmiObject -computerName $vm.__server -NameSpace "root\virtualization" -Query "Select * From MsVM_ResourceAllocationSettingData Where instanceId Like 'Microsoft:$($vm.name)%' and resourceSubtype = 'Microsoft Synthetic Diskette Drive'").__Path
     $diskRASD.connection=$VFDPath
     $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
     $arguments = @($VM.__Path, @( $diskRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
     $Result=$VSMgtSvc.psbase.invokeMethod("AddVirtualSystemResources", $arguments)
     if ($result -eq 0) {"Added floppy to VM '$($vm.elementname)'." }  else {"Failed to add floppy to VM'$($vm.elementname)', result code: $Result." }}
}
#Example: add-VMFloppyDisk $core "C:\Users\Public\Documents\Microsoft Hyper-V\Blank Floppy Disk\blank.VFD"  
#         Adds a floppy disk to the machine Pointed to by $Core , the VFD being Blank.vfd.      


Filter Get-VMFloppyDisk
{Param ($VM , $server="."  )
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Get-VMFloppyDisk -VM $_  }}
 if ($VM -is [System.Management.ManagementObject]) {Get-WmiObject -computerName $vm.__server -NameSpace "root\virtualization" -Query "Select * From MsVM_ResourceAllocationSettingData Where instanceId Like 'Microsoft:$($vm.name)%' and resourceSubtype = 'Microsoft Virtual Floppy Disk'"}
 $vm = $null
}
#Example: Get-VMFloppyDisk (get-vm -server james-2008) | foreach {$_.connection} 
#         Dumps a list of all the VFD files in the floppy drives of VMs on the server "James-2008"

########################This function is properly tested yet and support status is unknown :-) Use at your own risk
Function get-VmBackupScript
{Param ($vm="%", $BackupDevice="e:\" , $SERVER=".", [switch]$dataFiles )
 $ImgMgtSvc=Get-WmiObject -computerName $server -NameSpace  "root\virtualization" -Class "MsVM_ImageManagementService"
 
 $diskPaths=(list-vmdisk -vm $vm -snapshot | foreach {$_.diskpath}) | select-object -unique          
 $diskpaths += $Diskpaths | Get-VHDInfo | forEach {$_.parentPath} | select-object -unique


 If ($Datafiles) {$Datapaths = (Get-WmiObject -NameSpace "root\virtualization" -class Msvm_VirtualSystemGlobalSettingData| foreach-object {$_.SnapshotDataRoot ; $_.ExternalDataRoot } )| sort |  select-object -unique }
 else {$Datapaths = @()}      

 $vols=&{foreach ($volume in (get-wmiobject -computerName $server -class Win32_volume)) {
                 if ( ((($diskpaths + $dataPaths) | where{$_ -match $volume.name.replace("\","\\")})     -ne $null) -or 
                      ((($diskpaths + $dataPaths) | where{$_ -match $volume.DeviceID.replace("\","\\")}) -ne $null) ) {$volume} } }
 foreach ($vol in $vols) {
   $vn          = $vol.name
   $vid         = $vol.DeviceID
   $vnRegEx     = $vol.name.replace("\","\\").replace("?","\?")
   $vidRegEx    = $vol.DeviceID.replace("\","\\").replace("?","\?")
   $destination = $BackupDevice + ($vol.__Server) + $Vol.DeviceID.SPLIT("?")[1] 

   "echo Delete Shadows Volume $vn                             > Script.dsh"
   "echo Set Context Persistent                               >> Script.dsh "
   "echo Begin Backup                                         >> Script.dsh "
   "echo Writer Verify {66841cd4-6ded-4f4b-8f17-fd23f8ddc3de} >> Script.dsh "
   "echo Add Volume $VN ALIAS MyShadow                        >> Script.dsh "
   "echo Create                                               >> Script.dsh "
   "echo End Backup                                           >> Script.dsh "
   'echo Expose %MyShadow% X:                                 >> Script.dsh '
   "echo Exec CopyVM.CMD                                      >> Script.dsh "
   "echo Unexpose X:                                          >> Script.dsh "
   "echo Exit                                                 >> Script.dsh "

   "echo MD ""$destination"" > CopyVM.CMD" 

   $Datapaths | where {$_ -match "^$vnRegEx"}  | foreach {$CopyFrom=$_.replace($vn,'X:\')
							  $CopyTo  =$_.replace($vn,$destination)
							  "echo MD """+ $CopyTo.Substring(0,$copyto.lastIndexof("\")) + """   >> copyVM.CMD"
							  "echo RoboCOPY ""$CopyFrom""  ""$Copyto"" /E >> CopyVM.Cmd " }
   $Datapaths | where {$_ -match "^$vidRegEx"} | foreach {$CopyFrom=$_.replace($vn,'X:\')
							  $CopyTo  =$_.replace($vn,$destination)
							  "echo MD """+ $CopyTo.Substring(0,$copyto.lastIndexof("\")) + """   >> copyVM.CMD"
							  "echo RoboCOPY ""$CopyFrom""  ""$Copyto"" /E >> CopyVM.Cmd " }
   

   $diskpaths | where {$_ -match "^$vnRegEx"}  | foreach {$CopyFrom=$_.replace($vn,'X:\')
							  $CopyTo  =$_.replace($vn,$destination)
							  "echo MD """+ $CopyTo.Substring(0,$copyto.lastIndexof("\")) + """   >> copyVM.CMD"
							  "echo COPY ""$CopyFrom""  ""$Copyto""  >> CopyVM.Cmd " }
   $diskpaths | where {$_ -match "^$vidRegEx"} | foreach {$CopyFrom=$_.replace($vid,'X:\')
							  $CopyTo  =$_.replace($vid,$destination)
							  "echo MD """+ $CopyTo.Substring(0,$copyto.lastIndexof("\")) + """   >> copyVM.CMD"
							  "echo COPY ""$CopyFrom""  ""$Copyto""  >> CopyVM.Cmd " }
   
   "DiskShadow /s Script.dsh "   
  }
  "Exit"
}

#example 1: Get-VmBackupScript -Server Hyper-Core -BackupDevice R: |  winrs -r:hyperCore cmd.exe
#           Gets a backup script to copy the VHDs and ISOs for all vms on the server "hyper-core" to its drive R:, and runs it by piping it into a WINRS session. 
#example 2: Get-VmBackupScript -datafiles |  CMD
#           Gets a backup script to copy the VHDs, ISOs and Datafiles for all vms on the local server and runs it by piping it into CMD.exe 

###############################################################
#
# Functions for working with Networking, (NICS, switches and ports on switches that nics connect to)
#
###############################################################

Function Choose-VMSwitch
{Param ($server="." )
 choose-list  (Get-WmiObject -computerName $server -NameSpace  "root\virtualization" -Class "MsVM_VirtualSwitch") @(@{Label="Switch Name"; Expression={$_.ElementName}} )
}
#Example: Choose-VmSwitch -Server James-2008
#         Prompts the user to choose from the available switches on the server James-2008


Filter Get-VMNic
{Param ($VM, $server="." ,  [switch]$Legacy, [switch]$VMBus)
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array]) {if ($legacy) {$VM | ForEach-Object {Get-VmNic -VM $_ -legacy} }
                       if ($vmbus)  {$VM | ForEach-Object {Get-VmNic -VM $_ -VMbus } } }
 if ($VM -is [System.Management.ManagementObject]) {
    if ($legacy) {Get-WmiObject -computerName $vm.__server -NameSpace "root\virtualization" -Query "Select * From MsVM_EmulatedEthernetPortSettingData Where instanceId Like 'Microsoft:$($vm.name)%' " }
    if ($vmbus)  {Get-WmiObject -computerName $vm.__server -NameSpace "root\virtualization" -Query "Select * From MsVM_SyntheticEthernetPortSettingData Where instanceId Like 'Microsoft:$($vm.name)%' "}}
 $vm = $null
}
#Example: Get-VMNic $core -legacy -vmbus
#         Returns both Legacy and VMbus NICs found on the VM pointed to by $core


Filter Get-VMNicport
{Param ($nic) 
 if ($nic -eq $null) {$nic=$_}
 if ($nic -is [System.Management.ManagementObject]) {
   Get-WmiObject -computerName $nic.__server -NameSpace "root\virtualization" -Query "Select * From Msvm_SwitchPort  where __Path='$( $nic.connection[0].replace('\','\\') )'" }
 $nic=$null
}
#Example: Get-VMNic $core -legacy -vmbus | get-vmNicPort
#         Returns the SwitchPorts on the NICs of the VM pointed to by $core 


Filter Get-VMnicSwitch
{Param ($nic)
 if ($nic -eq $null) {$nic=$_}
 if ($nic -is [System.Management.ManagementObject]) {
     Get-WmiObject -computerName $nic.__server  -NameSpace "root\virtualization" -Query "ASSOCIATORS OF {$((Get-VMNicPort $nic).__Path)} where resultclass = Msvm_VirtualSwitch" }
 $nic = $null
}
#Example:  (Get-VMNic $vm -legacy -vmbus | get-vmNicSwitch) | foreach-object {$_.elementName}
#         Returns the Switches used by the VM pointed to by $core 



Filter List-VMNic
{Param ($vm)
 if ($vm -eq $null) {$vm=$_}
 foreach ($v in $vm) { 
       if ($V -is [String]) {$V=(Get-VM -Machinename $VM) }
       Foreach ($nic in (get-vmnic -VM $v -legacy -vmbus)) {
           $(if ((get-VMNicPort -nic $nic) -eq $null) {"   "} 
             else {Get-VMNicSwitch -nic $nic} ) | select-object @{name="VM"; expression={$v.elementName }},
                                                                @{name="MACAddress"; expression={$nic.Address}},
                                                                @{name="Type"; expression={$nic.ResourceSubType}},
                                                                @{name="Network"; expression={$_.ElementName}}       }}
 $vm = $null
}
#Example: get-vm | List-VMNic | format-table -autosize
#         Returns a formatted table show the NICs on all VMs of the local server, with their MAC address and network connection. 


Filter Choose-VMNIC
{param ($vm, $server=".")
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [System.Management.ManagementObject]) {
     choose-list (get-vmnic $vm -legacy -vmbus) @("ResourceSubType", "address", @{label="Network"; expression={(get-vmnicSwitch $_).elementname}}) }
}
#Example: choose-vmnic $Core
#          Allows the user to choose from the NICs on the server pointed to by $core

Filter Add-VMNIC
{Param ($VM , $Virtualswitch, $mac, $server=".", [switch]$legacy )
 if ($VM -eq $null) {$VM=$_}
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($VM -is [Array]) {if ($legacy) {$VM | ForEach-Object {add-VmNic -VM $_ -Virtualswitch $Virtualswitch -legacy} }
                              else  {$VM | ForEach-Object {add-VmNic -VM $_ -Virtualswitch $Virtualswitch} } }
 if ($VM -is [System.Management.ManagementObject]) {
     # As before We're going to call ADD Virtual System Resources, and we need set up the parameter array...
     # Create the correct Resource Allocation Setting Data object    
     if ($Legacy) {$NicRASD = NEW-VMRasd -resType 10 -resSubType 'Microsoft Emulated Ethernet Port' -server $vm.__Server 
                   $NicRASD.ElementName= "Legacy Network Adapter"}  
     else         {$NicRASD = NEW-VMRasd -resType 10 -resSubType 'Microsoft Synthetic Ethernet Port' -server $vm.__Server 
                   $NicRASD.VirtualSystemIdentifiers=@("{"+[System.GUID]::NewGUID().ToString()+"}")
                   $NicRASD.ElementName= "VMBus Network Adapter"}     
     if ($virtualSwitch -ne $null) {$Newport = new-VmSwitchport $virtualSwitch
                                    if ($Newport -eq $null) {$Newport= ""}
                                    $NicRASD.Connection= $newPort}
      if ($mac -ne $null) {$nicRasD.address = $mac
			   $nicRasD.StaticMacAddress = $true }
      #Now calling AddVirtualSystemResources as usual
      $arguments = @($VM.__Path, @( $nicRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
      $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
      $result = $VSMgtSvc.psbase.invokeMethod("AddVirtualSystemResources", $arguments) 
      if ($result  -eq 0) {"Added NIC to '$($VM.elementname)'."} else {"Failed to add NIC to '$($VM.elementname)', return code: $Result" }}
  $vm = $null
}
#Example 1: Add-VMNIC $tenby (choose-VMswitch)
#         adds a VMbus nic to the server  choosing the connection from a list of switches
#Example 2: Add-VMNIC $tenby (choose-VMswitch) -legacy
#         adds a Legacy nic to the server  choosing the connection from a list of switches
#Example 3: get-vm core-% -Server James-2008 | add-vmnic -virtualSwitch "Internal Virtual Network" -legacy
#         Adds a legacy nic to those VMs on Server James-2008 which have names beginning Core- and binds them to "Internal virtual network"


Function Set-VMNICConnection
{Param ($VM , $nic, $Virtualswitch, $Server=".") 
  # Same process as in Add-VMNIC
  if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
  if ($virtualSwitch -ne $null) {$Newport = new-VmSwitchPort $virtualSwitch}
  if ($Newport -eq $null) {$Newport= ""}
  $Oldport=Get-WmiObject -computerName $vm.__server -NameSpace "root\virtualization" -Query "Select * From Msvm_SwitchPort  where __Path='$( $nic.connection[0].replace('\','\\') )' "
  if ($oldPort -ne $null) {
       $SwitchMgtSvc=(Get-WmiObject -computerName $vm.__server -NameSpace  "root\virtualization" -Query "Select * From MsVM_VirtualSwitchManagementService")
       $arguments=@($oldport)
       $result = $SwitchMgtSvc.psbase.invokeMethod("DeleteSwitchPort",$arguments)  
       if ($Result -eq 0 ) {"Removed Switch port."} else {"Failed to remove switch port."}}  
  $nic.connection = $newPort
  $arguments = @($VM.__Path, @( $nic.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null )                                                                                                                                                                                             
  $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
  $result=$VSMgtSvc.psbase.invokeMethod("ModifyVirtualSystemResources", $arguments) 
  if ($Result -eq 0 ) {"Changed network connection on VM '$($vm.elementName)'."} else {"Failed to change Network connection on VM '$($vm.elementName)'."}
}
#Example: Set-VMNICConnection $core (choose-vmNic $core) (choose-VMswitch $core.__server)
#         Re-connects a NIC on the VM pointed to by $core, if there are multiple NICs the user will prompted to select one, and they will be prompted to select a switch


Function Set-VMNICAddress
{Param($vm, $nic, $mac)
         $nic.address=$mac 
	 $nic.staticMacAddress=$true
         $arguments = @($VM.__Path, @( $nic.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null )
         $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
         $result=$VSMgtSvc.psbase.invokeMethod("ModifyVirtualSystemResources", $arguments) 
         if ($Result -eq 0 ) {"Set Mac Address on VM '$($vm.elementName)'."} else {"Failed to Set Mac Address on VM '$($vm.elementName)'."}
}
#Example: Set-VMNICAddress $core (choose-vmNic $core) "00155D010101"
#         Sets the MAC address of a NIC on the pointed to by $core, if there are multiple NICs the user will prompted to select one


Function New-VMSwitchPort
{Param ($virtualSwitch , $Server=".") 
 if ($Virtualswitch -is [String]) {$Virtualswitch=(Get-WmiObject -computerName $server -NameSpace "root\virtualization" -Query "Select * From MsVM_VirtualSwitch Where elementname = '$Virtualswitch' ")}
 if ($Virtualswitch -is [System.Management.ManagementObject])  {
    $SwitchMgtSvc=(Get-WmiObject -computerName $Virtualswitch.__server -NameSpace  "root\virtualization" -Query "Select * From MsVM_VirtualSwitchManagementService")
      # We're going to call Create Switch Port, and we need set up the parameter array - following info is copied from MSDN
      #  uint32 CreateSwitchPort(
      #    [in]   MsVM_SwitchService SwitchService,   // The name of a Switch 
      #    [in]   string Name,                        // A guid 
      #    [in]   string FriendlyName,                // Usually "network adapter" or "legacy network adapter"
      #    [in]   string ScopeOfResidence,
      #   [out]  MsVM_SwitchPort CreatedSwitchPort    // Path to the created port
      #);

     [String]$GUID=[System.GUID]::NewGUID().ToString()
     $arguments=@($Virtualswitch.__Path, $GUID, $GUID, $null, $null)
     $result = $SwitchMgtSvc.psbase.invokeMethod("CreateSwitchPort",$arguments) 
     if ($result -eq 0) {"Created VirtualSwitchPort on '$($virtualSwitch.elementName)' " | out-host
                         @($arguments[4]) }
     else               {"Failed to create VirtualSwitchPort on '$($virtualSwitch.elementName)': return code: $Result" | out-host} }
}
#Not intended to be called directly, used by Add-VMNIC and SetVMNICConnection


Function Remove-VMNIC
{Param ($VM , $nic, $Server=".") 
 if ($VM -is  [String]) {$VM=(Get-VM -Machinename $VM -Server $Server) }
 if ($NIC -is [Array]) {$NIC | ForEach-Object {Remove-VMNICConnection -VM $VM -NIC $_ } }
 if (($VM -is [System.Management.ManagementObject]) -and ($nic -is [System.Management.ManagementObject]) ) {
	 Set-VMNICConnection -VM $vm -Nic $nic $null 
 	 $arguments = @($VM.__Path, @( $nic.__Path ), $null )                                                                                                                                                                                             
         $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
         $result=$VSMgtSvc.psbase.invokeMethod("RemoveVirtualSystemResources", $arguments) 
         if ($Result -eq 0 ) {"Removed NIC from VM '$($vm.elementName)'."} else {"Failed to remove NIC from VM '$($vm.elementName)'."}  }
}
#Example: Remove-VMNICConnection $core (choose-vmNic $core) 
#         Removes a NIC on server pointed to by $core, if there are multiple NICs the user will prompted to select one


Function Get-VMByMACaddress
{Param($mac, $Server=".")
 get-vm -server $server | List-VMNic | where {$_.macaddress -eq "$Mac"}
}
#Example 1: Get-VMbymacAddress "00155D000101"
#           Returns detils of the VM with the NIC given the address 00155D000101
#Example 2: get-vm (get-vmbyMacAddress  "00155DD0BEEF").vm
#            Returns the WMI object representing that VM. 


#########################################################################
#
# Functions for managing VM State (Snapshots and VM Export)
#
#########################################################################

#ToDo Export is not complete yet
Filter Export-VM
{Param ($VM , $Server=".", $path, [switch]$copyState)
 if ($VM -eq $null) {$VM=$_ }
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Get-VMSnapshot -VM $_ -Server $server} }
 if ($VM -is [System.Management.ManagementObject]) {  
   $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
   $arguments=@($VM.__path,($CopyState.Ispresent),$path,$null)   
   $Result=$VSMgtSvc.psbase.invokeMethod("ExportVirtualSystem", $arguments)  }
$result
$vm=$null
}


Filter Get-VMSnapshot
{Param ($VM="%" , $Server=".")
 if ($VM -eq $null) {$VM=$_ }
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {Get-VMSnapshot -VM $_ -Server $server} }
 if ($VM -is [System.Management.ManagementObject]) {
    Get-WmiObject -computerName $vm.__server -NameSpace root\virtualization -Query "Select * From MsVM_VirtualSystemSettingData Where systemName='$($VM.name)' and instanceID <> 'Microsoft:$($VM.name)' " }
 $vm=$null
}
#Example: Get-Vmsnapshot $Core
#         Returns the snapshots on the VM pointed to by $core

Filter Get-VMSnapshotTree
{Param ($VM , $Server=".")
 if ($VM -eq $null) {$VM=$_ }
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $server) }
 if ($VM -is [System.Management.ManagementObject]) {
   $snapshots=(Get-VMSnapshot -VM $VM -Server $Server) 
   #need to check for 0 or 1 snapshots
   if ($snapshots -is [array]) {out-tree -items $snapshots -startAt ($snapshots | where{$_.parent -eq $null}) -path "__Path" -Parent "Parent" -label "elementname"} }
}
#Example: Get-VmsnapshotTree $Core
#         Returns the snapshots on the VM pointed to by $core and displays them as a tree


Filter Choose-VMSnapshot
{Param ($VM , $Server=".")
 if ($VM -eq $null) {$VM=$_ }
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $server) }
 if ($VM -is [System.Management.ManagementObject]) {
 	$snapshots=(Get-VMSnapshot $VM)
	if ($snapshots -is [array]) {Choose-Tree -items $snapshots -startAt ($snapshots | where{$_.parent -eq $null}) -path "__Path" -Parent "Parent" -label "elementname"}
        else {$snapshots}  }
}
#Example: Choose-Vmsnapshot $Core
#         Gets the Snapshots of the machine pointed to by $core and if there are multiple snap shots prompst the user to select one from a tree 


Filter New-VMSnapshot
{Param( $VM , $Server=".")
 if ($VM -eq $null) {$VM=$_ }
 if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM -Server $server) }
 if ($VM -is [Array]) {$VM | ForEach-Object {New-VMSnapshot -VM  $_} }
 if ($VM -is [System.Management.ManagementObject]) {
      #We are going to invoke CreateVirtualSystemSnapshot. Following from MSDN
      #CreateVirtualSystemSnapshot. (
      #  [in]   CIM_ComputerSystem Ref SourceSystem,  // Reference to the virtual computer system to be snapshotted.
      #  [out]  CIM_VirtualSystemSettingData Ref SnapshotSettingData,  //The CIM_VirtualSystemSettingData instance that was created to represent the snapshot.
      #  [out]  CIM_ConcreteJob Ref Job );
      #If this method is executed synchronously, it returns 0 if it succeeds. 
      #If this method is executed asynchronously, it returns 4096 and the Job output parameter can be used to track the progress of the asynchronous operation.
      # Any other return value indicates an error.
      # Virtual system Setting data never seems to be set. 
     $arguments=($VM,$Null,$null) 
     $VSMgtSvc=Get-WmiObject -ComputerName $VM.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
     $result=$VSMgtSvc.psbase.invokeMethod("CreateVirtualSystemSnapshot",$arguments)
     "Snapshoting VM '$($vm.elementName)': "+ $ReturnCode[[string]$result] | Out-host
     $arguments[2] }
 $vm=$null
} 
#Example 1: new-vmsnapshot $Core 
#           Tkaes a snapshot of the VM pointed to by $core
#Example 2: get-vm "core%" -server "James-2008" | new-VmSnapshot
#           Gets the VMs with names beginning "Core" on the server "James-2008" and snapshot them 


Filter Remove-VMSnapshot
{Param( $snapshot , [Switch]$Tree )
 if ($snapshot -eq $null) {$snapshot=$_ }
 if ($snapshot -is [Array]) {$snapshot | ForEach-Object {New-VMSnapshot -snapshot  $snapshot} }
 if ($snapshot -is [System.Management.ManagementObject]) {
     #We are going to invoke RemoveVirtualSystemSnapshot. Following from MSDN
     #RemoveVirtualSystemSnapshot(
     #  [in]   CIM_VirtualSystemSettingData Ref SnapshotSettingData, // Reference to the CIM_VirtualSystemSettingData instance which represents the snapshot to be removed.
     #  [out]  CIM_ConcreteJob Ref Job );
     #If this method is executed synchronously, it returns 0 if it succeeds. 
     #If this method is executed asynchronously, it returns 4096 and the Job output parameter can be used to track the progress of the asynchronous operation.  
     #Any other return value indicates an error.
     $arguments=($snapshot,$Null) 
     $VSMgtSvc=Get-WmiObject -ComputerName $snapshot.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
     if ($tree) {$result=$VSMgtSvc.psbase.invokeMethod("RemoveVirtualSystemSnapshotTree",$arguments) }
     else       {$result=$VSMgtSvc.psbase.invokeMethod("RemoveVirtualSystemSnapshot",$arguments) }
     "Removing Snapshot(s): " + $ReturnCode[[string]$result] | Out-host
     $arguments[1] }
 $snapShot=$null
}
#Example: choose-vmsnapshot $Core | remove-vmsnapshot -tree
#         Lets the user select a snapshot on the VM pointed to by $core and removes it and any children it may have

Filter Apply-VMSnapshot
{Param($SnapShot)
 if ($snapshot -eq $null) {$snapshot=$_ }
 if ($snapshot -is [System.Management.ManagementObject]) {
     $VM = Get-WmiObject -computername $snapshot.__server -NameSpace "root\virtualization" -Query ("Select * From MsVM_ComputerSystem Where Name='$($Snapshot.systemName)' " )
  #We are going to call ApplyVirtualSystemSnapshot - the following is from MSDN
  #  [in]   CIM_ComputerSystem Ref ComputerSystem, // Reference to the CIM_ComputerSystem instance to which the snapshot should be applied.
  #  [in]   CIM_VirtualSystemSettingData Ref SnapshotSettingData, // Reference to the CIM_VirtualSystemSettingData instance that represents the snapshot to be applied.
  #  [out]  CIM_ConcreteJob Ref Job );
  #If this method is executed synchronously, it returns 0 if it succeeds. 
  #If this method is executed asynchronously, it returns 4096 and the Job output parameter can be used to track the progress of the asynchronous operation. 
  #Any other return value indicates an error.
     $arguments=@($VM,$snapshot,$null)
     $VSMgtSvc=Get-WmiObject -ComputerName $snapshot.__server -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"
     $result=$VSMgtSvc.psbase.invokeMethod("ApplyVirtualSystemSnapshot", $arguments)
     if ($result -eq 0) {"Snapshot Applied" | out-host }
     elseif ($result -eq 4096) {"Job Started to apply snapshot" | out-host
                                $arguments[2]}
     else {"failed" | Out-Host} }
} 
#Example: choose-vmsnapshot $Core | Apply-vmsnapshot 
#         Lets the user select a snapshot on the VM pointed to by $core and applies it.


#########################################################################
#
# List functions added - they all have nouns begin VM or VHD
#
#########################################################################


"VM Functions added"
dir Function: | Where {($_.name -match "-VM") -or ($_.name -match "-VHD")} | select name | sort name
#[that gives a list by verb, this will give it sorted by noun] dir Function: | Where {($_.name -match "-VM") -or ($_.name -match "-VHD")} | select name | sort @{expression={$_.name.split("-")[1]}}, name




 

